package cscie160.hw6;


import java.net.*;

import java.io.*;

import java.util.StringTokenizer;
import java.util.HashSet;
import java.util.LinkedList;

/**

 * This class manages the simplest demo operations with the ATM on server side.

 * It serves as the ATM asynchronous request-dispatching engine
*/

public class Server
{
    private ServerSocket serverSocket;

    private ATM atmImplementation;

    private BufferedReader bufferedReader;

    private LinkedList<ATMRunnable> requestQueue;
    private LinkedList<ATMThread> threads;
    private final int defaultThreadsQuant = 5;


    
    /**

     * main method to collect command line input parameters and launch the server
     * side operations. 
     * @param argv String[]
    */

    public static void main(String argv[])
    {

        int port = 1099;

        if(argv.length > 0)
        {

            try
            {
                port = Integer.parseInt(argv[0]);

            }
            catch (Exception e)
            { 

                e.printStackTrace();

            }

        }

        try
        {

            System.out.println("Server is ABOUT to be created");
            Server server = new Server(port);

            //server.serviceClient();
            System.out.println("Client is ABOUT to be serviced");
            server.serviceClient2();
            System.out.println("Client serviced");
        }
        catch (Exception ex)
        {

            ex.printStackTrace();
        }

    }

    /**
    * 1-arg constructor creates Server socket on required port if possible.
    * Then create the pool of Threads that will service incoming requests.
    */
    public Server(int port) throws java.io.IOException {

        requestQueue = new LinkedList<ATMRunnable>(); 
        createThreads(defaultThreadsQuant);
        serverSocket = new ServerSocket(port);
        atmImplementation = new ATMImplementation();
    }



    /**
    * creates and start the threads.
    * the threads are added to the threads pool
    */
    private void createThreads(int threadsQ)
    {
        threads = new LinkedList<ATMThread>();
        for (int tn = 0; tn < threadsQ; tn++)
        {
            ATMThread th = new ATMThread(requestQueue);
            threads.add(th);
            th.start();
            System.out.println(getClass().getName() + ": method createThreads. Thread " + th.getName() + " started.");
        }
    }

    /**

     *  accepts a client connection and reads lines from the socket.

     *  Each line is used to create a work-order object with the details
     *  read from the client and to put the object in a collection for processing by one of
     *  the Threads created at startup

     */

    public void serviceClient2() throws java.io.IOException
    {

        System.out.println("Accepting clients now");

        Socket clientConnection = serverSocket.accept();

	// Arrange to read input from the Socket
        InputStream inputStream = clientConnection.getInputStream();

        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));


        // Arrange to write result across Socket back to client

        OutputStream outputStream = clientConnection.getOutputStream();

        PrintStream printStream = new PrintStream(outputStream);



        System.out.println("Starting server socket on port #"
            + serverSocket.getLocalPort() + ", reading from socket");


        try    
        {

            String commandLine = null;
            while ((commandLine = bufferedReader.readLine()) != null)
            {
                System.out.println(getClass().getName() + " method serviceClient2. commandLine="+commandLine);


                ATMRunnable atmRunnable = new ATMRunnable();
                    
                atmRunnable.setCommandLine(commandLine);
                atmRunnable.setPrintStream(printStream );
                atmRunnable.setAtm(atmImplementation);

                synchronized(requestQueue) {
                    System.out.println(getClass().getName() + " method=serviceClient2. requestQueueSize="+
                        requestQueue.size());

                    requestQueue.addLast(atmRunnable);

                    System.out.println(getClass().getName() + " method=serviceClient2. requestQueueSize="+
                        requestQueue.size());

                    requestQueue.notifyAll();
                    System.out.println(getClass().getName() + " method=serviceClient2. requestQueueSize="+
                        requestQueue.size());
                }
 
            }

        }
        catch (SocketException sException)
        {

            // client has stopped sending commands.  Exit gracefully.

            System.out.println("Done because of " + sException.getMessage());

        }

   }

    /**

     *  serviceClient accepts a client connection and reads lines from the socket.

     *  Each line is handed to executeCommand for parsing and execution.

     */

    /*public void serviceClient() throws java.io.IOException
    {

        System.out.println("Accepting clients now");

        Socket clientConnection = serverSocket.accept();

	// Arrange to read input from the Socket
        InputStream inputStream = clientConnection.getInputStream();

        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));


        // Arrange to write result across Socket back to client

        OutputStream outputStream = clientConnection.getOutputStream();

        PrintStream printStream = new PrintStream(outputStream);


        System.out.println("Starting server socket on port #"
            + serverSocket.getLocalPort() + ", reading from socket");


        try    
        {

            String commandLine;

            while ((commandLine = bufferedReader.readLine()) != null)
            {

                try
                {

                    Float result = executeCommand(commandLine);

                    // Only BALANCE command returns non-null

                    	
                }
                catch (ATMException atmex)
                {

                    System.out.println("ERROR: " + atmex);

                }
 
            }

        }
        catch (SocketException sException)
        {

            // client has stopped sending commands.  Exit gracefully.

            System.out.println("done");

        }

    }
*/

    /** The logic here is specific to our protocol.  We parse the string

    *  according to that protocol.

    */

    /*private Float executeCommand(String commandLine) throws ATMException
    {

        // Break out the command line into String[]

        StringTokenizer tokenizer = new StringTokenizer(commandLine);

        String commandAndParam[] = new String[tokenizer.countTokens()];

        int index = 0;

        while (tokenizer.hasMoreTokens())
        {

            commandAndParam[index++] = tokenizer.nextToken();

        }

        String command = commandAndParam[0];

        // Dispatch BALANCE request without further ado.

        if (command.equalsIgnoreCase(Commands.BALANCE.toString()))
        {

            return atmImplementation.getBalance();

        }

        // Must have 2nd arg for amount when processing DEPOSIT/WITHDRAW commands

        if (commandAndParam.length < 2)
        {

            throw new ATMException("Missing amount for command \"" + command + "\"");

        }

        try
        {

            float amount = Float.parseFloat(commandAndParam[1]);

            if (command.equalsIgnoreCase(Commands.DEPOSIT.toString()))
            {

                atmImplementation.deposit(amount);

            }
            else if (command.equalsIgnoreCase(Commands.WITHDRAW.toString()))
            {

                atmImplementation.withdraw(amount);

            }
            else
            {

                throw new ATMException("Unrecognized command: " + command);

            }

        }
        catch (NumberFormatException nfe)
        {
            throw new ATMException("Unable to make float from input: " + commandAndParam[1]);

        }

        // BALANCE command returned result above.  Other commands return null;

        return null;
    }

*/
 }
